package com.eight.common.utils.enumeration;

import org.apache.http.util.Asserts;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.stream.Stream;

public class GenericUtils {
    private static GenericUtils instance = null;

    private GenericUtils() {
    }

    public static GenericUtils instance() {
        if (instance == null) {
            Class var0 = GenericUtils.class;
            synchronized(GenericUtils.class) {
                if (instance == null) {
                    instance = new GenericUtils();
                }
            }
        }

        return instance;
    }

    public Class<?> get1stGenericCls(Object obj) {
        return this.getGenericCls(obj, 1, (Class)null);
    }

    public Class<?> get1stGenericCls(Object obj, Class<?> parentClassOrInterface) {
        return this.getGenericCls(obj, 1, parentClassOrInterface);
    }

    public Class<?> getGenericCls(Object obj, int idx, Class<?> parentClassOrInterface) {
        return this.getGenericClses(obj, parentClassOrInterface)[idx - 1];
    }

    private Class<?>[] getGenericClses(Object obj) {
        ParameterizedType pType = this.getParameterizedType(obj.getClass());
        return pType != null ? (Class[])Stream.of(pType.getActualTypeArguments()).map((t) -> {
            return (Class)t;
        }).toArray((x$0) -> {
            return new Class[x$0];
        }) : null;
    }

    public Class<?>[] getGenericClses(Object obj, Class<?> parentClassOrInterface) {
        this.validate(obj, parentClassOrInterface);
        if (parentClassOrInterface == null) {
            return this.getGenericClses(obj);
        } else {
            ParameterizedType pType = this.getParameterizedType(parentClassOrInterface, obj.getClass());
            return pType != null ? (Class[])Stream.of(pType.getActualTypeArguments()).map((t) -> {
                return (Type)(t instanceof ParameterizedType ? ((ParameterizedType)t).getRawType() : (Class)t);
            }).toArray((x$0) -> {
                return new Class[x$0];
            }) : null;
        }
    }

    private void validate(Object obj, Class<?> parentClassOrInterface) {
        Asserts.notNull(obj, "param: [obj] must not be null.");
        Class<?> cls = obj.getClass();
        Asserts.check(!Object.class.equals(cls), "param: [obj].class must not be Object.class");
        Asserts.check(cls.getInterfaces().length != 0 || !Object.class.equals(cls.getSuperclass()), "obj在没有实现任何接口时不能是Object的直接子类。");
        if (parentClassOrInterface != null) {
            Asserts.check(!Object.class.equals(parentClassOrInterface), "param: [" + parentClassOrInterface + "] must not be Object.");
            Asserts.check(parentClassOrInterface.isAssignableFrom(cls), "param: obj(" + obj + ") must be subClass of [" + parentClassOrInterface + "].");
        }

    }

    private ParameterizedType getParameterizedType(Class<?> cls) {
        ParameterizedType pType = null;
        if (!cls.isInterface()) {
            pType = (ParameterizedType)cls.getGenericSuperclass();
            if (Object.class.equals(pType.getRawType()) && cls.getGenericInterfaces().length > 0) {
                pType = (ParameterizedType)cls.getGenericInterfaces()[0];
            }
        } else if (cls.getGenericInterfaces().length > 0) {
            pType = (ParameterizedType)cls.getGenericInterfaces()[0];
        }

        return pType;
    }

    private ParameterizedType getParameterizedType(Class<?> parentClassOrInterface, Class<?> cls) {
        ParameterizedType pType = null;
        if (!parentClassOrInterface.isInterface()) {
            pType = (ParameterizedType)cls.getGenericSuperclass();
        } else {
            pType = (ParameterizedType)Stream.of(cls.getGenericInterfaces()).filter((t) -> {
                return parentClassOrInterface.equals(((ParameterizedType)t).getRawType());
            }).findFirst().get();
        }

        return pType;
    }
}
